package com.ubxtech.web.handler;

import cn.hutool.core.lang.SimpleCache;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.ubxtech.core.context.Result;
import com.ubxtech.core.plugins.Page;
import com.ubxtech.core.utils.AesUtil;
import com.ubxtech.core.utils.AttrReplaceUtil;
import com.ubxtech.core.utils.Sm4Util;
import com.ubxtech.web.annotation.EncryptEnum;
import com.ubxtech.web.annotation.IgnoreResponseBodyAdvice;
import com.ubxtech.web.annotation.ResponseAppendOssPrefix;
import com.ubxtech.web.annotation.ResponseEncrypt;
import com.ubxtech.web.properties.AuthProperties;
import com.ubxtech.web.utils.LocaleUtil;
import com.ubxtech.web.utils.ReflectMethodUtil;
import com.ubxtech.web.utils.WebUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;

/**
 * @author Victor.Xiao
 * @since 2022-01-14 16:42
 **/
@Slf4j
@ControllerAdvice(annotations = RestController.class)
public class GlobalResponseBodyHandler implements ResponseBodyAdvice<Object> {
    @Resource
    private AuthProperties authProperties;
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        //swagger 3.0是用了RestController,所以拦截了 MultipleOpenApiWebMvcResource
        if (methodParameter.getDeclaringClass().toString().toLowerCase().contains("swagger")
                ||methodParameter.getDeclaringClass().toString().toLowerCase().contains("springfox")
                ||methodParameter.getDeclaringClass().toString().contains("OpenApiWebMvcResource")) {
            return false;
        }
        // 类上如果被 IgnoreResponseBodyAdvice 标识就不拦截
        if (methodParameter.getDeclaringClass().isAnnotationPresent(IgnoreResponseBodyAdvice.class)) {
            return false;
        }
        if (methodParameter.getMethod() == null) {
            return false;
        }
        // 方法上被标注也不拦截
        if (methodParameter.getMethod().isAnnotationPresent(IgnoreResponseBodyAdvice.class)) {
            return false;
        }
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (body == null) {
            return null;
        }
        Result<Object> result = null;
        if (body instanceof Result) {
            result = (Result) body;
        } else {
            result =Result.ok(body);
        }
        LocaleUtil.replaceI18n(result);
        //加密结果
        replaceResponseEncrypt( methodParameter, result);

        //这里接口如果是string,还必须得转换成string,但奇怪的是int就不用转,直接返回result就行
        if(body instanceof String) {
            return JSON.toJSONString(result);
        }
        //统一修改状态码,方便errorDecoder异常捕获
        if (result.getCode() != null && result.getCode() != 200) {
            serverHttpResponse.setStatusCode(WebUtil.resolveCode(result.getCode()));
        }
        replaceOssUrl(result);
        return result;
    }

    /**
     * 加密结果
     * @author Victor.Xiao
     * @since 2023-09-05 14:20
     * @param methodParameter methodParameter
     * @param result result
    */
    private void replaceResponseEncrypt(MethodParameter methodParameter, Result<Object> result) {
        if (methodParameter==null || methodParameter.getMethod() == null) {
            return;
        }
        ResponseEncrypt responseEncrypt = null;
        // 类上或者方法上如果被 ResponseEncrypt 标识拦截
        if (methodParameter.getMethod().isAnnotationPresent(ResponseEncrypt.class)) {
            ResponseEncrypt[] responseEncrypts = methodParameter.getMethod().getDeclaredAnnotationsByType(ResponseEncrypt.class);
            if (responseEncrypts != null && responseEncrypts.length > 0) {
                responseEncrypt = responseEncrypts[0];
            }
        }
        // 类上或者方法上如果被 ResponseEncrypt 标识拦截
        if (responseEncrypt ==null && methodParameter.getDeclaringClass().isAnnotationPresent(ResponseEncrypt.class)) {
            ResponseEncrypt[] responseEncrypts = methodParameter.getDeclaringClass().getDeclaredAnnotationsByType(ResponseEncrypt.class);
            if (responseEncrypts.length > 0) {
                responseEncrypt = responseEncrypts[0];
            }
        }
        //类上或者方法上是加密整个对象,只有AES和SM4
        if (responseEncrypt != null) {
            EncryptEnum encryptEnum = responseEncrypt.type();
            if (encryptEnum == EncryptEnum.AES) {
                result.setData(AesUtil.encode(JSON.toJSONString(result.getData()),authProperties.getResponseSecret()));
            } else{
                result.setData(Sm4Util.encryptHex(JSON.toJSONString(result.getData()),authProperties.getResponseSecret()));
            }
        }
        //其他则再判断字段是不是有标记
        else {
            replaceResponseEncrypt(result);
        }
    }

    /**
     * 数据脱敏和加密
     * @author Victor.Xiao
     * @since 2024-07-18 14:39
     * @param result result
     */
    private void replaceResponseEncrypt(Result<Object> result) {
        Object object = result.getData();
        if (object instanceof Page<?> page) {
            object = page.getRecords();
        }
        if (object instanceof List list) {
            for (Object obj : list) {
                replaceResponseEncrypt(obj);
            }
        }
        else {
            replaceResponseEncrypt(result.getData());
        }
    }

    /**
     * 数据脱敏和加密
     * @author Victor.Xiao
     * @since 2024-07-18 14:39
     * @param result result
    */
    private void replaceResponseEncrypt(Object result) {
        try {
            List<Field> fields = ReflectMethodUtil.getAnnotationField(result,ResponseEncrypt.class);
            for (Field field : fields) {
                if (field.isAnnotationPresent(ResponseEncrypt.class)) {
                    ResponseEncrypt responseEncrypt = field.getAnnotation(ResponseEncrypt.class);
                    Object value = ReflectUtil.getFieldValue(result, field);
                    if (value != null) {
                        //空字符串也不处理
                        if (StrUtil.isBlank(value.toString())) {
                            continue;
                        }
                        field.set(result, AttrReplaceUtil.serialize(value.toString(), responseEncrypt));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            log.error("字段脱敏出错",e);
        }
    }

    /**
     * 统一替换url前缀
     * @author Victor.Xiao
     * @since 2023-09-05 11:57
     * @param result result
     */
    private void replaceOssUrl(Result<Object> result) {
        if (result != null && result.getData() != null) {
            Object object = result.getData();
            if (object instanceof Page<?> page) {
                object = page.getRecords();
            }
            if (object instanceof List list) {
                for (Object obj : list) {
                    replaceOssUrl(obj);
                }
            }else {
                replaceOssUrl(result.getData());
            }
        }
    }

    /**
     * 统一替换url前缀
     * @author Victor.Xiao
     * @since 2023-09-05 11:57
     * @param result result
    */
    private void replaceOssUrl(Object result) {
        try {
            List<Field> fields = ReflectMethodUtil.getAnnotationField(result,ResponseAppendOssPrefix.class);
            for (Field field : fields) {
                if (field.isAnnotationPresent(ResponseAppendOssPrefix.class)) {
                    ResponseAppendOssPrefix responseAppendOssPrefix = field.getAnnotation(ResponseAppendOssPrefix.class);
                    Object value = ReflectUtil.getFieldValue(result, field);
                    if (value != null) {
                        //空字符串也不处理
                        if (StrUtil.isBlank(value.toString())) {
                            continue;
                        }
                        //如果是个完整的就不拼接
                        if (value.toString().startsWith("http") || value.toString().startsWith("HTTP")) {
                            continue;
                        }
                        field.set(result, (StrUtil.isBlank(responseAppendOssPrefix.prefix()) ? authProperties.getOssPrefixUrl() : responseAppendOssPrefix.prefix()) + value);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            log.error("解析oss的prefix出错",e);
        }
    }
}
